﻿using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using CkSoftware.GroupMe.Sdk.Core;
using CkSoftware.GroupMe.Sdk.Core.Dao;
using CkSoftware.GroupMe.Sdk.Core.Exceptions;
using CkSoftware.GroupMe.Sdk.Core.Interfaces;
using CkSoftware.GroupMe.Sdk.Groups.Commands;

namespace CkSoftware.GroupMe.Sdk.Groups.Dao
{
	/// <summary>
	/// The DAO for the list of groups available for the currently authenticated user. Implements <see cref="IPagedCollection{GroupMeGroup}" />, so this interface can be used for paging.
	/// </summary>
	public class GroupsListing : IPagedCollection<GroupMeGroup>
	{
		private readonly List<GroupMeGroup> _fetchedEntities;
		private readonly GroupIndexCommand _indexCommand;
		private readonly IWebApiAccess _webApiAccess;

		private List<GroupMeGroup> _lastFetchedEntities;

		/// <summary>
		/// Instantiates a new <see cref="GroupsListing" /> object using the given webApiAccess, indexCommand and initial entity results.
		/// </summary>
		/// <param name="webApiAccess">The <see cref="IWebApiAccess" /> which should be used to call the API.</param>
		/// <param name="indexCommand">The (already used) <see cref="GroupIndexCommand" /> for furter use (paging).</param>
		/// <param name="initialEntityResults">The results of the first <see cref="GroupIndexCommand" /> execution.</param>
		public GroupsListing(IWebApiAccess webApiAccess,
			GroupIndexCommand indexCommand,
			IEnumerable<GroupData> initialEntityResults)
		{
			List<GroupMeGroup> initialEntities =
				initialEntityResults.Select(data => new GroupMeGroup(webApiAccess, data)).ToList();
			_fetchedEntities = new List<GroupMeGroup>(initialEntities);
			_lastFetchedEntities = new List<GroupMeGroup>(initialEntities);

			_webApiAccess = webApiAccess;
			_indexCommand = indexCommand;
		}

		/// <summary>
		/// Returns an enumerator that iterates through the collection.
		/// </summary>
		/// <returns>
		/// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
		/// </returns>
		/// <filterpriority>1</filterpriority>
		public IEnumerator<GroupMeGroup> GetEnumerator()
		{
			return _fetchedEntities.GetEnumerator();
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return GetEnumerator();
		}

		/// <summary>
		/// List of all entities fetched.
		/// </summary>
		public IEnumerable<GroupMeGroup> Entries
		{
			get { return _fetchedEntities; }
		}

		/// <summary>
		/// List of entities fetched in the last FetchNext call.
		/// </summary>
		public IEnumerable<GroupMeGroup> NewEntries
		{
			get { return _lastFetchedEntities; }
		}

		/// <summary>
		/// Fetch the next page for the paged entities.
		/// </summary>
		/// <returns>
		/// The result of the fetch, false if there were no more entities fetched, true if there were new entities
		/// available.
		/// </returns>
		public async Task<bool> FetchNext()
		{
			_indexCommand.ForNextPage();
			GroupMeApiResponse<GroupData[]> results = await _webApiAccess.ExecuteCommand(_indexCommand);

			if (results.Meta.ParsedCode != GroupMeApiResultCode.Ok)
			{
				throw new GroupMeException(results.Meta);
			}

			_lastFetchedEntities = new List<GroupMeGroup>(results.Response.Select(data => new GroupMeGroup(_webApiAccess, data)));
			_fetchedEntities.AddRange(_lastFetchedEntities);

			return _lastFetchedEntities.Any();
		}

		/// <summary>
		/// Fetches all entities until there are no more entities available.
		/// </summary>
		/// <returns>The awaitable <see cref="Task" />.</returns>
		public async Task FetchAll()
		{
			while (await FetchNext())
			{
			}
		}
	}
}